﻿using System;

using System.Collections.Generic;
using System.Xml;
using System.Globalization;

namespace PFD
{

    public class GeometryArray : IArrayElementModifier<float>
    {
        public float[] array;
        bool bDirty;

        public bool IsDirty
        {
            get { return bDirty; }
        }

        public void ResetDirtyFlag()
        {
            bDirty = false;
        }

        public int Length
        {
            get { return array.Length; }
        }

        #region IArrayElementModifier<float> Members

        public float this[long nNumber]
        {
            get
            {
                return array[nNumber];
            }
            set
            {
                if (array[nNumber] != value)
                    bDirty = true;
                array[nNumber] = value;
            }
        }

        #endregion
    }

    public class SimpleActivator : ISystemVarConnector<bool>
    {
        public bool active;

        public SimpleActivator(bool DefaultState)
        {
            active = DefaultState;
        }

        #region ISystemVarConnector<bool> Members

        public void DoUpdate(bool newVal)
        {
            active = newVal;
        }

        #endregion
    }

    public class SystemVarConnector<T> : ISystemVarConnector<T>
            where T : IConvertible
    {
        protected IArrayElementModifier<T> updateObject;
        protected long elementObject;
        protected float PostOffs;

        public SystemVarConnector(IArrayElementModifier<T> u, long n, float PostProcOffs)
        {
            updateObject = u;
            elementObject = n;
        }

        public void DoUpdate(T newVal)
        {
            updateObject[elementObject] = (T)Convert.ChangeType(Convert.ToDouble(PostOffs) + newVal.ToDouble(NumberFormatInfo.CurrentInfo), typeof(T), NumberFormatInfo.CurrentInfo);
        }
    }

    public class LinearEquationConnector : ISystemVarConnector<float>
    {
        protected IArrayElementModifier<float> updateObject;
        protected long elementObject;
        protected float offset = 1.0f;
        protected float delta = 1.0f;
        protected float normalize = 1.0f;
        protected float Offs;    // post processing offset

        public LinearEquationConnector(IArrayElementModifier<float> objectToUpdate, long indexOfElement, float fOffset, float fDelta, float fNormalize, float PostProcessingOffset)
        {
            Offs = PostProcessingOffset;
            updateObject = objectToUpdate;
            elementObject = indexOfElement;
            offset = fOffset;
            delta = fDelta;
            normalize = fNormalize;
        }

        public void DoUpdate(float newVal)
        {
            updateObject[elementObject] = (offset + Offs + delta * newVal) / normalize;
        }
    }

    public class LinearStripesOnTexture : ISystemVarConnector<float>
    {
        //  this one modifies a squaresized texturelayer of type TRIANGLESTRIP
        protected IVertexBuffer updateObject;
        protected VertexElementType ved;
        protected float offset = 1.0f;
        protected float delta = 1.0f;
        protected float normalize = 1.0f;
        protected float stripsize = 1.0f;
        protected float viewportsizey = 1.0f;
        protected float viewportsizex = 1.0f;
        protected bool isVertical = true;
        protected long U1, U2, U3, U4, V1, V2, V3, V4;

        protected long OffsU, OffsV;

        public LinearStripesOnTexture(IVertexBuffer updObj, VertexElementType vertElementType, float fOffset, float fDelta, float fNormalize, float texsize, float tsizex, float tsizey, bool bVert, long tOffsetU, long tOffsetV)
        {
            OffsU = tOffsetU;
            OffsV = tOffsetV;
            updateObject = updObj;
            offset = fOffset;
            delta = fDelta;
            normalize = fNormalize;
            ved = vertElementType;
            if ((ved != VertexElementType.TextureSet1Coordinates) &&
                (ved != VertexElementType.TextureSet2Coordinates))
                throw new ArgumentOutOfRangeException("vertElementType", "Parameter can only be one of the two TextureSetCoordinate values!");
            stripsize = texsize;
            viewportsizex = tsizex;
            viewportsizey = tsizey;
            isVertical = bVert;
            VertexElementDimension vedBaseU, vedBaseV;
            if (ved == VertexElementType.TextureSet1Coordinates)
            {
                vedBaseU = VertexElementDimension.U1;
                vedBaseV = VertexElementDimension.V1;
            }
            else
            {
                vedBaseU = VertexElementDimension.U2;
                vedBaseV = VertexElementDimension.V2;
            }

            //  Prepare indices so this does not have to be done everytime at runtime 
            U1 = updateObject.GetElementBufferIndex(vedBaseU, 0);
            U2 = U1 + updateObject.stride;
            U3 = U2 + updateObject.stride;
            U4 = U3 + updateObject.stride;
            V1 = updateObject.GetElementBufferIndex(vedBaseV, 0);
            V2 = V1 + updateObject.stride;
            V3 = V2 + updateObject.stride;
            V4 = V3 + updateObject.stride;
        }

        public void DoUpdate(float newVal)
        {
            if (isVertical)
            {
                //  Make linear equation
                float upper = offset + delta * newVal - viewportsizey / 2.0f;
                float xleft = (float)Math.Floor(upper / stripsize) * viewportsizex;
                float xright = xleft + viewportsizex;
                float lower = upper % stripsize + viewportsizey;
                upper = upper % stripsize;
                updateObject[U1] = updateObject[U2] = xleft / normalize + OffsU;
                updateObject[V1] = updateObject[V4] = lower / normalize + OffsV;
                updateObject[V2] = updateObject[V3] = upper / normalize + OffsV;
                updateObject[U3] = updateObject[U4] = xright / normalize + OffsU;
            }
            else
            {
                float left = offset + delta * newVal - viewportsizex/2.0f;
                float upper = (float)Math.Floor(left / stripsize) * viewportsizey;
                float lower = upper + viewportsizey;
                float right = left % stripsize + viewportsizex;
                left = left % stripsize;
                updateObject[U1] = updateObject[U2] = (OffsU + left) / normalize;
                updateObject[V1] = updateObject[V4] = (OffsV + lower) / normalize;
                updateObject[V2] = updateObject[V3] = (OffsV + upper) / normalize;
                updateObject[U3] = updateObject[U4] = (OffsU+right) / normalize;
            }
        }
    }

    public class LinearDigitStripesOnTexture : ISystemVarConnector<float>
    {
        //  this one modifies a squaresized texturelayer of type TRIANGLESTRIP
        protected IVertexBuffer updateObject;
        protected VertexElementType ved;
        protected int vardigit = 1;
        protected float offset = 1.0f;
        protected float delta = 1.0f;
        protected float normalize = 1.0f;
        protected float tilesizey = 1.0f;
        protected float tilesizex = 1.0f;
        protected long U1, U2, U3, U4, V1, V2, V3, V4;
        protected long OffsU, OffsV;

        public LinearDigitStripesOnTexture(IVertexBuffer updObj, VertexElementType vertElementType, int vd, float fOffset, float fDelta, float fNormalize, float ty, float tx, long tOffsU, long tOffsV)
        {
            OffsU = tOffsU;
            OffsV = tOffsV;
            updateObject = updObj;
            ved = vertElementType;
            if ((ved != VertexElementType.TextureSet1Coordinates) &&
                (ved != VertexElementType.TextureSet2Coordinates))
                throw new ArgumentOutOfRangeException("vertElementType", "Parameter can only be one of the two TextureSetCoordinate values!");
            vardigit = vd;
            offset = fOffset;
            delta = fDelta;
            normalize = fNormalize;
            tilesizex = tx;
            tilesizey = ty;

            VertexElementDimension vedBaseU, vedBaseV;
            if (ved == VertexElementType.TextureSet1Coordinates)
            {
                vedBaseU = VertexElementDimension.U1;
                vedBaseV = VertexElementDimension.V1;
            }
            else
            {
                vedBaseU = VertexElementDimension.U2;
                vedBaseV = VertexElementDimension.V2;
            }

            //  Prepare indices so this does not have to be done everytime at runtime 
            U1 = updateObject.GetElementBufferIndex(vedBaseU, 0);
            U2 = U1 + updateObject.stride;
            U3 = U2 + updateObject.stride;
            U4 = U3 + updateObject.stride;
            V1 = updateObject.GetElementBufferIndex(vedBaseV, 0);
            V2 = V1 + updateObject.stride;
            V3 = V2 + updateObject.stride;
            V4 = V3 + updateObject.stride;
        }
        
        #region ISystemVarConnector<float> Members

        public void DoUpdate(float newVal)
        {
            int nNumber = Convert.ToInt32(newVal) / vardigit;
            int nDigit =  nNumber % 10;
            float Upper, Lower;

            if ((nDigit < 2) && ((nNumber > 1) || (nNumber < -1)))
                nDigit += 10;
            if (nNumber < 0)
                Upper = offset - delta * nDigit - tilesizey/2.0f;
            else
                Upper = offset + delta * nDigit - tilesizey/2.0f;
            if (Upper < 0.0f)
                Upper = 0.0f;
            Lower = Upper + tilesizey;
            if (Lower > normalize)
                Lower = normalize;

            updateObject[U1] = updateObject[U2] = OffsU / normalize;
            updateObject[U3] = updateObject[U4] = (tilesizex + OffsU)/ normalize;
            updateObject[V1] = updateObject[V4] = (Lower + OffsV )/ normalize;
            updateObject[V2] = updateObject[V3] = (Upper + OffsV) / normalize;
        }

        #endregion
    }
}
